package net.xdclass.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import net.xdclass.enums.BizCodeEnum;
import net.xdclass.enums.SendCodeEnum;
import net.xdclass.exception.BizException;
import net.xdclass.feign.CouponFeignService;
import net.xdclass.interceptor.LoginInterceptor;
import net.xdclass.mapper.UserMapper;
import net.xdclass.model.LoginUser;
import net.xdclass.model.UserDO;
import net.xdclass.request.NewUserCouponRequest;
import net.xdclass.request.RefreshTokenRequest;
import net.xdclass.request.UserLoginRequest;
import net.xdclass.request.UserRegisterRequest;
import net.xdclass.response.LoginResponse;
import net.xdclass.service.NotifyService;
import net.xdclass.service.UserService;
import net.xdclass.util.CommonUtil;
import net.xdclass.util.JsonData;
import net.xdclass.util.JwtUtil;
import net.xdclass.vo.UserVo;
import org.apache.commons.codec.digest.Md5Crypt;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @Classname UserServiceImpl
 * @Description TODO
 * @Date 2022/4/13 16:29
 * @Created by sunhao
 */
@Slf4j
@Service
public class UserServiceImpl implements UserService {

  @Autowired
  private UserMapper userMapper;

  @Autowired
  private NotifyService notifyService;

  @Autowired
  private StringRedisTemplate redisTemplate;

  @Autowired
  private CouponFeignService couponFeignService;

  /**
   * 用户注册
   * * 邮箱验证码验证
   * * 密码加密
   * * 账户唯一性检查
   * * 保存到数据库
   * * 新注册用户福利发放
   */
  @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
//  @GlobalTransactional  //开启分布式事务
  @Override
  public JsonData userRegister(UserRegisterRequest userRegisterRequest) {
    if (!notifyService.checkCode(SendCodeEnum.USER_REGISTER, userRegisterRequest.getMail(), userRegisterRequest.getCode())) {
      throw new BizException(BizCodeEnum.CODE_ERROR);
    }

    if (!checkUnique(userRegisterRequest.getMail())) {
      return JsonData.buildResult(BizCodeEnum.ACCOUNT_REPEAT);
    }
    UserDO userDO = assignment(userRegisterRequest);
    userMapper.insert(userDO);
    userRegisterInitTask(userDO);

    return JsonData.buildSuccessMessage("注册成功");
  }


  /**
   * 校验账户唯一
   *
   * @param mail
   * @return
   */
  private boolean checkUnique(String mail) {
    QueryWrapper<UserDO> queryWrapper = new QueryWrapper<UserDO>().eq("mail", mail);
    List<UserDO> userDOS = userMapper.selectList(queryWrapper);
    return userDOS.size() > 0 ? false : true;
  }

  /**
   * 对象赋值
   *
   * @param userRegisterRequest
   * @return
   */
  private static UserDO assignment(UserRegisterRequest userRegisterRequest) {
    UserDO userDO = new UserDO();
    BeanUtils.copyProperties(userRegisterRequest, userDO);
    userDO.setCreateTime(new Date());
    //生成随机 盐
    userDO.setSecret("$1$" + CommonUtil.getStringNumRandom(8));
    //密码加密
    userDO.setPwd(Md5Crypt.md5Crypt(userRegisterRequest.getPwd().getBytes(StandardCharsets.UTF_8), userDO.getSecret()));
    return userDO;
  }

  /**
   * 用户注册 初始化福利信息
   *
   * @param userDO
   */
  private void userRegisterInitTask(UserDO userDO) {
    NewUserCouponRequest newUserCouponRequest = new NewUserCouponRequest();
    newUserCouponRequest.setUserId(userDO.getId());
    newUserCouponRequest.setName(userDO.getName());
    JsonData jsonData = couponFeignService.newUserCoupon(newUserCouponRequest);
    if(jsonData.getCode() != 0){
      throw new RuntimeException("发放优惠券失败!");
    }
    log.info("发放新用户注册优惠券{} 结果{}",newUserCouponRequest,jsonData.toString());
  }


  /**
   * 用户登录
   *
   * @param userLoginRequest
   * @return
   */
  @Override
  public JsonData userLogin(UserLoginRequest userLoginRequest) {
    UserDO userDO = userMapper.selectOne(new QueryWrapper<UserDO>().eq("mail", userLoginRequest.getUserName()));

    if (null != userDO) {
      String requestPwd = Md5Crypt.md5Crypt(userLoginRequest.getPassWord().getBytes(StandardCharsets.UTF_8), userDO.getSecret());
      if (!requestPwd.equals(userDO.getPwd())) {
        return JsonData.buildResult(BizCodeEnum.ACCOUNT_PWD_ERROR);
      }
      //登录成功 生成token
      LoginUser loginUser = new LoginUser();
      BeanUtils.copyProperties(userDO,loginUser);
      String accessToken = JwtUtil.geneJsonWebToken(loginUser);
      String refreshToken = CommonUtil.generateUUID();
      redisTemplate.opsForValue().set(refreshToken,accessToken,JwtUtil.EXPIRE, TimeUnit.MILLISECONDS);
      return JsonData.buildSuccess(LoginResponse.getInstance(accessToken,refreshToken,JwtUtil.EXPIRE));
    }
    return JsonData.buildResult(BizCodeEnum.ACCOUNT_UNREGISTER);
  }
  @Override
  public JsonData refreshToken(RefreshTokenRequest refreshTokenRequest) {
    String accessToken = redisTemplate.opsForValue().get(refreshTokenRequest.getRefreshToken());
    if(StringUtils.isEmpty(accessToken)){
      return JsonData.buildResult(BizCodeEnum.TOKEN_EXPIRATION_TIME);
    }
    if(!accessToken.equals(refreshTokenRequest.getAccessToken())){
      return JsonData.buildResult(BizCodeEnum.TOKEN_ERROR);
    }
    Claims claims = JwtUtil.checkJwt(accessToken);
    LoginUser loginUser = LoginUser.transformation(claims);
    accessToken = JwtUtil.geneJsonWebToken(loginUser);
    String refreshToken = CommonUtil.generateUUID();
    redisTemplate.opsForValue().set(refreshToken,accessToken,JwtUtil.EXPIRE, TimeUnit.MILLISECONDS);
    redisTemplate.delete(refreshTokenRequest.getRefreshToken());
    return JsonData.buildSuccess(LoginResponse.getInstance(accessToken,refreshToken,JwtUtil.EXPIRE));
  }


  @Override
  public UserVo findUserDetail() {
    LoginUser loginUser = LoginInterceptor.threadLocal.get();
    UserDO userDO = userMapper.selectOne(new QueryWrapper<UserDO>().eq("id", loginUser.getId()));
    UserVo userVo = new UserVo();
    BeanUtils.copyProperties(userDO,userVo);
    return userVo;
  }
}
